%% @doc The Robber Agent decieds which tile to place the robber on when the AI throws a seven
%% or if it play a Knight card. To do this it comunicate with all nodes to get information
%% about the placement of the opponents settelements and cities.
-module(robber).
-export([init/0,getP_Tiles/4,getNodes/2,getBestTile/3]).

%% @doc The init function spawns the robber agent
%% @spec init() -> Pid
%% @end
init() -> spawn(fun() -> robber([],0) end).

%IDs = [{}{}{}{}{nodes[{nr,id}]}]
robber(IDs,RobberPos) -> 
    receive 
        {initIDs, NewIDs} -> robber(NewIDs,RobberPos);
        {newRobberPos,NewRobberPos} -> robber(IDs,NewRobberPos);
        {play_robber,Tiles,RID} ->
            Nodes = bot:getValue(nodes,IDs),
            io:fwrite("\tRobber: Fetch all possible tiles to put on~n"),
            P_Tiles = getP_Tiles(Tiles,Nodes,RobberPos,[]),
            io:fwrite("\tRobber: Select best tile~n"),
            Best_Tile = getBestTile(0,P_Tiles,0),
            io:fwrite("\tRobber: Bets tile is ~w~n",[Best_Tile]),
            RID!{best_tile_id,Best_Tile},
            robber(IDs,RobberPos);
		{robberPos,Pid} -> Pid ! {robberPos,RobberPos},
							robber(IDs,RobberPos)
    end.

%% @doc Checks which tiles that it's possible to put the robber on. 
%% If it is possible, then it will calculate a scale value based
%% on how many settlements/cities are connected to the tile and
%% how many players.
%% @spec getP_Tiles(Tiles,NodeIDs,Robber_Pos,PossiblePlacment) -> [Tiles]
%% @end
getP_Tiles([],_,_,P_Tiles) -> P_Tiles;
getP_Tiles([{Nr,Res,_Prob}|Tiles],Nodes,Robber_Pos,P_Tiles) ->
    %We can't put the robber on the same tile again and we don't wan't to place it
    %in the desert. Water tiles are also of limit. 
    if 
        Robber_Pos == Nr -> getP_Tiles(Tiles,Nodes,Robber_Pos,P_Tiles);
        Res == 0         -> getP_Tiles(Tiles,Nodes,Robber_Pos,P_Tiles);
        Res > 5          -> getP_Tiles(Tiles,Nodes,Robber_Pos,P_Tiles);
        true             -> 
			T_Nodes = getNodes(Nr,Nodes),
			Checked = check_owner(T_Nodes),
			if
				Checked -> getP_Tiles(Tiles,Nodes,Robber_Pos,P_Tiles);
				true    -> 
					{Players,Num_settlements} =  set_num_settlements(
						T_Nodes,[{0,false},{1,false},{2,false},{3,false}],0),
					Num_different = lists:sum(lists:map(
						fun({_,Set}) -> if Set -> 1; true -> 0 end end,Players)),
					%The scale value is higher if there are fewer players sharing the tile
					Robber_scale = Num_settlements/6 + (if Num_settlements > 0 -> 1 - Num_different/4;
															true -> 0 end),
					getP_Tiles(Tiles,Nodes,Robber_Pos,[{Nr,Robber_scale}|P_Tiles])
				end
	end.

%% @doc Sends a msg to all nodes around the current tile, asking for Owner and Type
%% Returns a list with this information
%% @spec getNodes(CurrentTileNumber,NodeIDs) -> [{Owner,Type}]
getNodes(Nr,Nodes) -> 
    AdjNodes = lists:map(fun(X)-> case bot:getValue(Nr+X,Nodes) of
                        false -> 0;
                        Nid   -> Nid!{robberInfo,self()}, 1
                       end end, [1,18,33,16,-1,-16]),   
    receiveNodeInfo([],lists:sum(AdjNodes)).

%We need awnsers from all 6 nodes
%This function recursively receives 6 times and the returns a list with node info
receiveNodeInfo(T_Nodes,C) ->
    receive {nodeInfo,Node} -> 
        if 
            C == 1 -> [Node|T_Nodes];
            true   -> receiveNodeInfo([Node|T_Nodes],C-1)
        end
    end.

%A function to easily get a value from a tuple list


%Checks if we have a settlement on one of the nodes surrunding a tile.
%Returns true if we have.
check_owner([]) -> false;
check_owner([{0,_}|_]) -> true;
check_owner([_|T_Nodes]) -> check_owner(T_Nodes).

%Checks if another player have a settlement on one of the nodes,
%and updates Players and Num_settlements acordingly.
set_num_settlements([],Players,Num_settlements) -> {Players,Num_settlements};
set_num_settlements([{Owner,Type}|T_Nodes],Players,Num_settlements) ->  
        if Owner /= -1 -> 
            set_num_settlements(T_Nodes,lists:keyreplace(Owner,1,Players,{Owner,true}),
                Num_settlements + Type);
           true -> set_num_settlements(T_Nodes,Players,Num_settlements)
        end.

%% @doc Goes through all possible tiles and 
%% returns the tile number that have the highest scale value 
%% @spec getBestTile(BestValue,Tiles,BestTileSoFar) -> TileNr
getBestTile(_,[],Tile) -> Tile;
getBestTile(BestScale,[{Nr,Scale}|P_Tiles],_) when Scale > BestScale -> 
    getBestTile(Scale,P_Tiles,Nr);
getBestTile(BestScale,[_|P_Tiles],Tile) -> getBestTile(BestScale,P_Tiles,Tile). 
